#pragma once
#include <string>
#include <map>
#include <vector>

class ZIniParser
{
public:
	typedef std::map<std::string, std::string> KeyValueMap;
	typedef std::map<std::string, KeyValueMap> SectionMap;

	/**
	\brief Serialize to the INI file format.
	*/
	std::string Serialize() const;

	/**
	\brief Deserialize from the INI file format.
	\param data The INI data.
	\param [out] errorLine The error line (only has a meaning when this function failed).
	\return true if it succeeds, false if it fails.
	*/
	bool Deserialize(const std::string & data, int & errorLine);

	/**
	\brief Sets a value. This will overwrite older values.
	\param section The section. Must have a length greater than one.
	\param key The key. Must have a length greater than one.
	\param value The value. Can be empty (effectively removing the value).
	\return true if the value was set successfully, false otherwise.
	*/
	bool SetValue(const std::string & section, const std::string & key, const std::string & value);

	/**
	\brief Removes all key/value pairs from a section.
	\param section The section to clear.
	\return true if it succeeds, false otherwise.
	*/
	bool ClearSection(const std::string & section);

	/**
	\brief Removes all sections.
	*/
	inline void Clear()
	{
		mSections.clear();
	}

	/**
	\brief Gets a value.
	\param section The section.
	\param key The key.
	\return The value. Empty string when the value was not found or empty.
	*/
	std::string GetValue(const std::string & section, const std::string & key) const;

	const KeyValueMap& GetValues(const std::string & section)const;
	/**
	\brief Gets the section names.
	\return List of section names.
	*/
	std::vector<std::string> Sections() const;

	/**
	\brief Gets keys in a given section.
	\param section The section.
	\return List of keys in the section. Empty if the section is not found or empty.
	*/
	std::vector<std::string> Keys(const std::string & section) const;

private:
	static const KeyValueMap emptyKeyValueMap;

	SectionMap mSections;

	enum LineType
	{
		Invalid,
		Empty,
		Section,
		KeyValue,
		Comment
	};

	static LineType getLineType(const std::string & line);
	static std::string trim(const std::string & str);
	static bool parseKeyValueLine(const std::string & line, std::string & key, std::string & value);
	static bool parseSectionLine(const std::string & line, std::string & section)
	{
		section = trim(line.substr(1, line.length() - 2));
		return section.length() > 0;
	}

	static inline void appendLine(std::string & output, const std::string & line)
	{
		if (output.length())
			output += "\r\n";
		output += line;
	}

	static inline std::string makeSectionText(const std::string & section)
	{
		return "[" + section + "]";
	}

	static inline std::string makeKeyValueText(const std::string & key, const std::string & value)
	{
		return key + "=" + escapeValue(value);
	}

	static inline bool needsEscaping(const std::string & value)
	{
		size_t len = value.length();
		return len && (value[0] == ' ' || value[len - 1] == ' ' ||
			value.find('\n') != std::string::npos ||
			value.find('\"') != std::string::npos);
	}
	static std::string escapeValue(const std::string & value);
	static std::string unescapeValue(const std::string & str);
};
